package com.ukefu.webim.service.repository;

import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang3.StringUtils;
import org.elasticsearch.action.search.SearchRequestBuilder;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.bucket.MultiBucketsAggregation;
import org.elasticsearch.search.aggregations.metrics.InternalNumericMetricsAggregation;
import org.elasticsearch.search.aggregations.metrics.NumericMetricsAggregation;

import com.ukefu.util.UKTools;
import com.ukefu.util.bi.CubeReportData;
import com.ukefu.util.bi.ReportData;
import com.ukefu.util.bi.model.FirstTitle;
import com.ukefu.util.bi.model.Level;
import com.ukefu.util.bi.model.ValueData;
import com.ukefu.webim.web.model.ColumnProperties;

public class ESCubeService {
	
	public CubeReportData execute(SearchRequestBuilder searchrequestbuilder) throws Exception{
		return execute(searchrequestbuilder , null,false) ;
	}
	
	public CubeReportData execute(SearchRequestBuilder searchrequestbuilder,List<ColumnProperties> cols,boolean isTol) throws Exception{
		CubeReportData cubeReportData = new CubeReportData();
		if (searchrequestbuilder == null) {
			return cubeReportData;
		}
		SearchResponse response = searchrequestbuilder.execute().actionGet();
		List<Aggregation> measureList = response.getAggregations().asList() ;
		if(measureList!=null){
			List<List<ValueData>> valuedatalist =  new ArrayList<List<ValueData>>();
			cubeReportData.setData(valuedatalist);
			Level row = new Level("root", "row" , null , 0);
			Level col = new Level("root", "col" , null , 0);
			cubeReportData.setRow(row);
			cubeReportData.setCol(col);
			row.setChilderen(new ArrayList<Level>());
			row.setTitle(new ArrayList<List<Level>>());
			row.getTitle().add(new ArrayList<Level>()) ;
			
			List<Level> titlesList = new ArrayList<Level>() ;
			
			List<FirstTitle> firstTitle = new ArrayList<FirstTitle>();
			boolean havecubeLevel = false;
			for(Aggregation terms :measureList){
				if(terms instanceof MultiBucketsAggregation){//设置标题
					firstTitle.add(new FirstTitle(terms.getName(), 1, false));
					havecubeLevel = true;
				}
			}
			List<ValueData> vdlist = new ArrayList<>();
			if(havecubeLevel){//是否有维度
				if (cols != null && cols.size() > 0) {
					for(ColumnProperties columnproperties : cols){
						firstTitle.add(new FirstTitle(columnproperties.getTitle(), 1, false));//把维度加到标题
					}
					row.setFirstTitle(firstTitle);
				}
				for(Aggregation terms :measureList){
					if (terms instanceof MultiBucketsAggregation) {
						processAggregations((MultiBucketsAggregation)terms, cubeReportData.getRow() , cubeReportData.getCol() , cubeReportData , cols, firstTitle);
					}
				}
			}
			if ((!havecubeLevel || isTol) && (cols != null && cols.size() > 0)) {
				Level tolrow = null;
				if (isTol) {
					tolrow = new Level("合计","row", null , 0);
					tolrow.setChilderen(new ArrayList<Level>());
					tolrow.setTitle(new ArrayList<List<Level>>());
					tolrow.getTitle().add(new ArrayList<Level>());
				}
				for(ColumnProperties columnproperties : cols){
					if (!havecubeLevel && !isTol) {
						firstTitle.add(new FirstTitle(columnproperties.getTitle(), 1, false));
					}
					for(Aggregation terms :measureList){
						if (terms.getName().equals(columnproperties.getTitle())) {
							Level title = new Level(terms.getName(), "row" , null , 1);
							if (tolrow != null) {
								title.setParent(tolrow);
								tolrow.getTitle().get(0).add(title);
							}else{
								title.setParent(row);
								row.getTitle().get(0).add(title);
							}
							InternalNumericMetricsAggregation.SingleValue svalue = (InternalNumericMetricsAggregation.SingleValue)terms ;
							ValueData value = null ;
							if(svalue.type().name().equals("count") || svalue.type().name().equals("value_count")){
								value = new ValueData(svalue.getName(), (int)svalue.value() , String.valueOf((int)svalue.value()), "number") ;
							}else{
								String foramatValue = String.valueOf(svalue.value());
								if("0".equals(columnproperties.getFormat()) && !StringUtils.isBlank(foramatValue)) {
									foramatValue = UKTools.getDuration(0, Long.parseLong(foramatValue)) ;
								}else if(!StringUtils.isBlank(columnproperties.getFormat())){
									foramatValue = new DecimalFormat(columnproperties.getFormat()).format(Double.parseDouble(foramatValue)) ;
								}
								value = new ValueData(svalue.getName(), svalue.value() , foramatValue, "number") ;
							}
							vdlist.add(processValueData(value,title));
							break;
						}
					}
				}
				if (tolrow != null) {
					tolrow.setParent(row);
					tolrow.setValueData(vdlist);
					cubeReportData.getRow().getChilderen().add(tolrow);
				}
				row.setFirstTitle(firstTitle);
			}
			valuedatalist.add(vdlist);
			
			cubeReportData.setData(valuedatalist);
			cubeReportData.getCol().setFirstTitle(firstTitle);
			cubeReportData.getCol().setTitle(new ArrayList<List<Level>>());
			cubeReportData.getCol().getTitle().add(titlesList);
			
			if(cubeReportData.getRow()==null){
				cubeReportData.setRow(new Level("root","row", null , 0)) ;
				cubeReportData.getRow().setTitle(new ArrayList<List<Level>>());
				if(cubeReportData.getRow().getTitle().size()==0){
					List<Level> rowList = new ArrayList<Level>() ;
					rowList.add(new Level("合计","row", null , 0)) ;
					cubeReportData.getRow().getTitle().add(rowList) ;
				}
			}
			cubeReportData.getRow().setTitle(new ArrayList<List<Level>>()) ;
			processTitle(cubeReportData.getRow() , cubeReportData.getRow());
		}
		
		return cubeReportData;
	}
	
	public ValueData processValueData(ValueData valueData , Level row){
		if (valueData != null) {
			valueData.setRow(row);
		}
		return valueData;
	}
	
	private int getDepth(Level level){
		int depth = 0 ;
		while((level = level.getParent())!=null){
			depth++ ;
		}
		return depth - 1 ;
	}
	
	/*
	 * 
	 */
	private void processTitle(Level level , Level root){
		for(int i=0 ; level.getChilderen()!=null && i<level.getChilderen().size() ; i++){
			Level child = level.getChilderen().get(i) ;
			int depth = getDepth(child) ;
			if(depth>=0){
				if(root.getTitle().size()<=depth){
					root.getTitle().add(new ArrayList<Level>()) ;
				}
				Level tempLevel = new Level(child.getName() , child.getNameValue() , child.getLeveltype() , child.getRowspan() , child.getColspan() , child.getValueData() , child.isTotal() , child.isFirst()) ;
				tempLevel.setParent(child) ;
				root.getTitle().get(depth).add(tempLevel)  ;
			}
			if((child.getNameValue().equals("UCKEFU_TOTAL") && root.getFirstTitle()!=null && (depth+1) < root.getFirstTitle().size()) || (child.getChilderen()==null && root.getFirstTitle()!=null && (depth+1) < root.getFirstTitle().size())){
				child.setChilderen(new ArrayList<Level>()) ;
				child.setColspan(root.getFirstTitle().size() - depth);
				if(root.getTitle()!=null && root.getTitle().size()>depth){
					for(Level title : root.getTitle().get(depth)){
						if(title.getName().equals(child.getName())){
							title.setColspan(child.getColspan()) ;
						}
					}
				}
				Level tempLevel = new Level("TOTAL_TEMP" , "UCKEFU_TOTAL" , child.getLeveltype() ,child.getRowspan() ,child.getColspan(), child.getValueData() , child.isTotal() , child.isFirst() , child.getDepth()+1) ;
				tempLevel.setParent(child) ;
				child.getChilderen().add(tempLevel) ;
				child.setValue(null) ;
			}
			processTitle(child , root);
		}
	}
	
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void iterator(Map<String, Map> value, Level level, String leveltype) {
		Iterator<String> iterator = value.keySet().iterator();
		if (level.getChilderen() == null) {
			level.setChilderen(new ArrayList<Level>());
		}
		while (iterator.hasNext()) {
			String name = iterator.next();
			Level sublevel = new Level(name, leveltype , level , level.getDepth()-1);
			level.getChilderen().add(sublevel);
			if (value.get(name) != null) {
				iterator(value.get(name), sublevel, leveltype);
			}
		}
	}
	
	public void processAggregations(MultiBucketsAggregation terms , Level row , Level col, ReportData reportData , List<ColumnProperties> measures , List<FirstTitle> firstTitleList){
		List<Level> titleList = null ;
		if(reportData.getRow().getTitle().size() <= row.getDepth()){
			reportData.getRow().getTitle().add(titleList = new ArrayList<Level>()) ;
		}else{
			titleList = reportData.getRow().getTitle().get(row.getDepth()) ;
		}
		/**
		 * 先处理 聚合的数据，然后再检查是否还有下级
		 */
		for(MultiBucketsAggregation.Bucket term : terms.getBuckets()){
			boolean inlist = false ;
			for(FirstTitle firstTitle : firstTitleList){
				if(firstTitle.getDepth() >= row.getDepth()){
					inlist = true; break ;
				}
			}
			if(!inlist){
				firstTitleList.add(new FirstTitle(terms.getName() , row.getDepth() , false)) ;
			}
			
			Level child = processLevelData(term, row);
			
			List<Aggregation> aggs = term.getAggregations().asList() ;
			List<ValueData> valueDataList = new ArrayList<ValueData>() ;
			
			titleList.add(child) ;
			
			if(aggs.size()>0){
				child.setChilderen(new ArrayList<Level>());
				
				for(ColumnProperties columnproperties : measures){
					for(Aggregation agg : aggs){
						if (agg.getName().equals(columnproperties.getTitle())) {
							String aggName = agg.getName() ;
							Aggregation aggregation = term.getAggregations().get(aggName) ;
							if(aggregation instanceof MultiBucketsAggregation){
								processAggregations((MultiBucketsAggregation)aggregation , child , col, reportData , measures , firstTitleList);
							}else{
								ValueData value = null ;
								if(aggregation instanceof NumericMetricsAggregation.SingleValue){
									child.setRowspan(1);
									child.setColspan(1);
									InternalNumericMetricsAggregation.SingleValue svalue = (InternalNumericMetricsAggregation.SingleValue) aggregation ;
									if(svalue.type().name().equals("count") || svalue.type().name().equals("value_count")){
										value = new ValueData(svalue.getName(), (int)svalue.value() , String.valueOf((int)svalue.value()), "number") ;
									}else{
										String foramatValue = String.valueOf(svalue.value());
										if("0".equals(columnproperties.getFormat()) && !StringUtils.isBlank(foramatValue)) {
											foramatValue = UKTools.getDuration(0, Long.parseLong(foramatValue)) ;
										}else if(!StringUtils.isBlank(columnproperties.getFormat())){
											foramatValue = new DecimalFormat(columnproperties.getFormat()).format(Double.parseDouble(foramatValue)) ;
										}
										value = new ValueData(svalue.getName(), processCompute(svalue.value(), aggName, measures) , foramatValue, "number") ;
									}
									value.setRow(new Level(term.getKey().toString(), "row", row , row.getDepth() + 1));
									value.setCol(new Level(aggName, "col", col, col.getDepth() + 1));
									valueDataList.add(value) ;
									if(child.getValueData()==null){
										child.setValueData(new ArrayList<ValueData>()) ;
									}
									child.getValueData().add(value) ;
								}else{
									value = new ValueData(term.getKey().toString(), term.getDocCount() , String.valueOf(term.getDocCount()), "number") ;
									value.setRow(new Level(term.getKey().toString(), "row", row , row.getDepth() + 1));
									value.setCol(new Level(term.getKey().toString(), "col", col , col.getDepth() + 1));
									
									valueDataList.add(value) ;
								}
							}
							break;
						}
		 			}
					
				}
				
				if(valueDataList.size()>0){
					reportData.getData().add(valueDataList) ;
				}
			}else{
				reportData.getData().add(valueDataList = new ArrayList<ValueData>()) ;
				
				ValueData value = new ValueData(term.getKey().toString(), term.getDocCount() , String.valueOf(term.getDocCount()), "number") ;
				@SuppressWarnings("unused")
				Level title = null ;
				value.setRow(title = new Level(term.getKey().toString(), "row", row , row.getDepth()+1));
				value.setCol(title = new Level(terms.getName(), "col", col , col.getDepth()+1));
				child.setRowspan(1);
				valueDataList.add(value) ;
			}
		}
		
	}
	
	/**
	 * 处理数值计算
	 * @param value
	 * @param aggName
	 * @param measures
	 * @return
	 */
	public double processCompute(double value , String aggName , List<ColumnProperties> measures){
		for(ColumnProperties properties : measures){
			if(properties.getTitle().equals(aggName)){
				value = Math.abs(value) ; break ;
			}
		}
		return value ;
	}
	/**
	 * 处理数值计算
	 * @param value
	 * @param aggName
	 * @param measures
	 * @return
	 */
	public String processFormat(double value , String aggName , List<ColumnProperties> measures){
		String formatValue = String.valueOf(value) ;
		for(ColumnProperties properties : measures){
			if(properties.getTitle().equals(aggName)){
				if(!StringUtils.isBlank(properties.getFormat())){
					DecimalFormat format = new DecimalFormat(properties.getFormat());
					formatValue = format.format(value) ; break ;
				}
			}
		}
		return formatValue ;
	}
	/**
	 * 聚合数据
	 * @param multiBucketsAggregation
	 * @param level
	 */
	public Level processLevelData(MultiBucketsAggregation.Bucket term, Level level){
		Level child = new Level(term.getKey().toString(), "row", level, level.getDepth()+1);
		level.getChilderen().add(child) ;
		return child ;
	}
}
